package com.nexus.plugin.redis.lock;

import com.nexus.core.exception.DistributedLockException;
import com.nexus.core.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Redis分布式锁Service
 *
 * @Date 2025/9/13 00:47
 * @Author luzhengning
 **/
@Slf4j
@Service
public class RedisLockService {

    @Autowired
    private RedissonClient redissonClient;

    private final ExpressionParser parser = new SpelExpressionParser();

    /**
     * 执行带有分布式锁保护的操作
     *
     * @param lockKey   锁的key
     * @param waitTime  等待锁的最长时间
     * @param leaseTime 锁的持有时间
     * @param timeUnit  时间单位
     * @param action    要执行的操作
     * @param <T>       返回类型
     * @return 操作结果
     * @throws DistributedLockException 获取锁失败时抛出
     * @Date 2025/9/13 02:22
     * @Author luzhengning
     **/
    public <T> T executeWithLock(String lockKey, long waitTime, long leaseTime, TimeUnit timeUnit, Supplier<T> action) throws DistributedLockException {
        RLock lock = redissonClient.getLock(lockKey);
        boolean isLocked = false;

        try {
            log.debug("尝试获取分布式锁，key: {}", lockKey);
            if (leaseTime <= 0) {
                isLocked = lock.tryLock(waitTime, timeUnit);
            } else {
                isLocked = lock.tryLock(waitTime, leaseTime, timeUnit);
            }

            if (isLocked) {
                log.debug("获取分布式锁成功，key: {}", lockKey);
                return action.get();
            } else {
                log.warn("获取分布式锁失败，key: {}", lockKey);
                throw new DistributedLockException("获取锁失败，请稍后重试", lockKey);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("获取分布式锁被中断，key: {}", lockKey, e);
            throw new DistributedLockException("获取锁被中断", lockKey, e);
        } finally {
            // 释放锁（仅在当前线程持有锁时释放）
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.debug("释放分布式锁，key: {}", lockKey);
            }
        }
    }

    /**
     * 执行带有分布式锁保护的操作（无返回值）
     *
     * @param lockKey   锁的key
     * @param waitTime  等待锁的最长时间
     * @param leaseTime 锁的持有时间
     * @param timeUnit  时间单位
     * @param action    要执行的操作
     * @throws DistributedLockException 获取锁失败时抛出
     * @Date 2025/9/13 02:22
     * @Author luzhengning
     **/
    public void executeWithLock(String lockKey, long waitTime, long leaseTime, TimeUnit timeUnit, Runnable action) throws DistributedLockException {
        executeWithLock(lockKey, waitTime, leaseTime, timeUnit, () -> {
            action.run();
            return null;
        });
    }

    /**
     * 带重试机制的分布式锁
     *
     * @Date 2025/9/13 03:26
     * @Author luzhengning
     **/
    public <T> T executeWithLock(String lockKey, long waitTime, long leaseTime,
                                 TimeUnit timeUnit, Supplier<T> action, int maxRetries)
            throws DistributedLockException {
        int retries = 0;
        while (retries <= maxRetries) {
            try {
                return executeWithLock(lockKey, waitTime, leaseTime, timeUnit, action);
            } catch (DistributedLockException e) {
                if (retries++ >= maxRetries) {
                    throw e;
                }
                log.debug("获取锁失败，进行第{}次重试", retries);
                try {
                    Thread.sleep(100); // 简单的等待策略
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new DistributedLockException("重试被中断", lockKey, ie);
                }
            }
        }
        throw new DistributedLockException("获取锁失败，超过最大重试次数", lockKey);
    }

    /**
     * 生成锁的key（基于注解和连接点）
     *
     * @param lockAnnotation 锁注解
     * @param joinPoint      连接点
     * @return 锁的key
     * @Date 2025/9/13 02:23
     * @Author luzhengning
     **/
    public String generateLockKey(RedisLock lockAnnotation, ProceedingJoinPoint joinPoint) {
        String keyTemplate = lockAnnotation.value();

        if (StringUtils.isEmpty(keyTemplate)) {
            String className = joinPoint.getTarget().getClass().getSimpleName();
            String methodName = ((MethodSignature) joinPoint.getSignature()).getMethod().getName();
            return "DistributedLock:" + className + ":" + methodName;
        }
        //解析SpEL表达式
        try {
            StandardEvaluationContext context = new StandardEvaluationContext();
            Object[] args = joinPoint.getArgs();
            String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();

            // 设置方法参数
            if (paramNames != null) {
                for (int i = 0; i < paramNames.length; i++) {
                    context.setVariable(paramNames[i], args[i]);
                }
            } else {
                // 处理参数名为空的情况
                for (int i = 0; i < args.length; i++) {
                    context.setVariable("arg" + i, args[i]);
                }
            }

            //设置方法信息
            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            context.setVariable("method", method);
            context.setVariable("target", joinPoint.getTarget());

            Expression expression = parser.parseExpression(keyTemplate);
            String dynamicKey = expression.getValue(context, String.class);
            return "DistributedLock:" + dynamicKey;
        } catch (Exception e) {
            log.warn("SpEL解析失败，使用原始字符串作为key: {}", keyTemplate, e);
            return "DistributedLock:" + keyTemplate;
        }

    }

    /**
     * 读锁：允许多个线程同时读
     *
     * @Date 2025/9/13 02:47
     * @Author luzhengning
     **/
    /**
     * 读锁：允许多个线程同时读，带超时和租期时间
     */
    public <T> T readLock(String key, long waitTime, long leaseTime, TimeUnit timeUnit, Supplier<T> action) throws DistributedLockException {
        RReadWriteLock rwLock = redissonClient.getReadWriteLock("ReadWriteLock:" + key);
        RLock lock = rwLock.readLock();
        boolean isLocked = false;
        try {
            isLocked = lock.tryLock(waitTime, leaseTime, timeUnit);
            if (isLocked) {
                return action.get();
            } else {
                throw new DistributedLockException("获取读锁失败", key);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new DistributedLockException("获取读锁被中断", key, e);
        } finally {
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 写锁：互斥，同一时间只能有一个线程写
     *
     * @Date 2025/9/13 02:49
     * @Author luzhengning
     **/
    public <T> T writeLock(String key, long waitTime, long leaseTime, TimeUnit timeUnit, Supplier<T> action) throws DistributedLockException {
        RReadWriteLock rwLock = redissonClient.getReadWriteLock("ReadWriteLock:" + key);
        RLock lock = rwLock.writeLock();
        boolean isLocked = false;
        try {
            isLocked = lock.tryLock(waitTime, leaseTime, timeUnit);
            if (isLocked) {
                return action.get();
            } else {
                throw new DistributedLockException("获取写锁失败", key);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new DistributedLockException("获取写锁被中断", key, e);
        } finally {
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


}
